Skip to content

Harden EKS deployment: Zero Trust pod security, NetworkPolicies, and policy-as-code CI gate#240

Open
devin-ai-integration[bot] wants to merge 5 commits into
DevOpsfrom
devin/1781279212-eks-zero-trust-hardening
Open

Harden EKS deployment: Zero Trust pod security, NetworkPolicies, and policy-as-code CI gate#240
devin-ai-integration[bot] wants to merge 5 commits into
DevOpsfrom
devin/1781279212-eks-zero-trust-hardening

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 12, 2026

Copy link
Copy Markdown

Summary

This PR applies one focused, reviewable Zero Trust hardening pass to the bankapp Kubernetes deployment (the actual EKS attack surface) and wires a policy-as-code gate into CI so the controls cannot silently regress.

Note on scope: the task referenced Terraform IaC, but this repo contains no Terraform — the IaC is the raw manifests in kubernetes/ plus a Helm chart in helm/, and CI is a Jenkins pipeline (Jenkinsfile) that calls a private Shared library (not in-repo, so it can't be run/validated here). The hardening therefore targets the Kubernetes manifests that are actually deployed via the GitOps CD pipeline.

Three controls, each with a regression gate:

  1. Hardened pod/container securityContext on bankapp and mysql deployments.
  2. NetworkPolicies — namespace default-deny ingress + least-privilege allows.
  3. Policy-as-code (Conftest/OPA) + Trivy IaC scan in a new GitHub Actions workflow.

Controls added → threat mitigated

Control Where Threat mitigated
runAsNonRoot: true, runAsUser/Group, fsGroup both deployments (pod) Container running as root → host/node compromise on breakout
seccompProfile.type: RuntimeDefault both deployments (pod) Unrestricted syscall surface used in container escapes
allowPrivilegeEscalation: false, privileged: false both containers In-container privilege escalation (setuid/CAP_SYS_ADMIN abuse)
readOnlyRootFilesystem: true (+ emptyDir for /tmp, and mysql runtime dirs) both containers Malware drop / persistence / tampering with the image filesystem
capabilities.drop: [ALL] both containers Abuse of default Linux caps (e.g. NET_RAW ARP/DNS spoofing)
default-deny-ingress NetworkPolicy namespace Lateral movement — pods accept traffic from anywhere by default
allow-bankapp-from-ingress-nginx (8080) bankapp Restricts north-south ingress to only the NGINX controller
allow-mysql-from-bankapp (3306) mysql DB only reachable from the app pods, not any compromised pod
Conftest gate + Trivy config gate in CI .github/workflows Prevents future manifests from regressing the above

Key implementation details

readOnlyRootFilesystem requires explicit writable mounts. The app only needs /tmp (JVM + embedded Tomcat scratch). MySQL additionally needs writable runtime dirs, mounted as emptyDir:

# bankapp
securityContext: {readOnlyRootFilesystem: true, ...}
volumeMounts: [{name: tmp, mountPath: /tmp}]
volumes: [{name: tmp, emptyDir: {}}]

# mysql (runs as uid/gid 999, the official image's mysql user)
volumeMounts: [/var/lib/mysql(PVC), /var/run/mysqld, /tmp, /var/lib/mysql-files]

The Conftest policy (policy/kubernetes-security.rego) enforces the container/pod controls per-document; policy/networkpolicy.rego runs in --combine mode to assert a default-deny ingress NetworkPolicy exists. Both have unit tests (*_test.rego).

Verification evidence (run locally)

Trivy IaC config scan (--severity HIGH,CRITICAL) on the two deployments:

BEFORE (origin/DevOps): 6 HIGH findings
  KSV-0118 default security context (bankapp + mysql, pod & container)
  KSV-0014 readOnlyRootFilesystem not set (bankapp + mysql)
AFTER  (this branch):   0 findings   ->  trivy config ... --exit-code 1  = pass

Conftest gate correctly fails closed on the un-hardened manifests and passes on the hardened ones:

$ conftest test /tmp/before/*.yml --policy policy
12 tests, 2 passed, 10 failures        # original manifests are rejected

$ conftest verify --policy policy                                  # 6 tests passed
$ conftest test kubernetes/bankapp-deployment.yml kubernetes/mysql-deployment.yml --policy policy   # 12 passed
$ conftest test --combine kubernetes/ --namespace netpol --policy policy                            # 1 passed
$ kubeconform -strict -ignore-missing-schemas kubernetes/                                           # Valid: 14/14

The same Conftest + Trivy commands run on every PR via the new IaC Security workflow.

Not in scope (follow-ups)

  • Mirroring the same controls into the helm/ chart templates.
  • Replacing the deprecated openjdk:17-alpine base image and remediating image-layer CVEs (the new Trivy gate makes these visible for a focused follow-up).
  • Runtime validation on a live EKS cluster (manifests are schema- and policy-validated; no cluster was available in this environment).

Link to Devin session: https://app.devin.ai/sessions/4313fb49ecd0461a9ffadd6e9a4caa08
Requested by: @achalc


Devin Review

Status Commit
⚪ Not started

Run Devin Review

💡 Connect your GitHub account to enable automatic code reviews.

Open in Devin Review (Staging)

…and policy-as-code CI gate

Co-Authored-By: Achal Channarasappa <achal.channarasappa@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration

Copy link
Copy Markdown
Author

Zero Trust hardening summary

This PR reduces the EKS attack surface with one focused, reviewable change plus a CI gate to keep it from regressing.

Controls added (control → threat mitigated):

  • Pod runAsNonRoot + seccompProfile: RuntimeDefault (bankapp & mysql) → blocks running as root and unrestricted syscalls used in container escapes.
  • Container allowPrivilegeEscalation: false, privileged: false → prevents in-container privilege escalation.
  • readOnlyRootFilesystem: true (with emptyDir for /tmp and mysql runtime dirs) → stops malware drop/persistence/filesystem tampering.
  • capabilities.drop: [ALL] → removes default Linux caps (e.g. NET_RAW spoofing).
  • NetworkPolicies: namespace default-deny-ingress + least-privilege allows (nginx → bankapp:8080, bankapp → mysql:3306) → blocks lateral movement; the DB is reachable only from app pods.
  • Policy-as-code gate (Conftest/OPA) + Trivy IaC scan in a new IaC Security GitHub Actions workflow → enforces all of the above on every PR.

Verification evidence (local):

  • Trivy IaC config (HIGH,CRITICAL) on the two deployments: 6 HIGH → 0 (KSV-0118 default security context ×4, KSV-0014 readOnlyRootFilesystem ×2).
  • Conftest fails closed on the pre-hardening manifests (10 failures) and passes on the hardened ones; conftest verify 6/6 unit tests pass; kubeconform -strict 14/14 valid.

Out of scope (follow-ups): mirror controls into the Helm chart, replace deprecated openjdk:17-alpine base image + remediate image CVEs (now visible via the Trivy gate), and live-cluster runtime validation (no Terraform exists in this repo; IaC is the K8s manifests).

devin-ai-integration Bot and others added 4 commits June 12, 2026 15:49
Co-Authored-By: Achal Channarasappa <achal.channarasappa@cognition.ai>
Co-Authored-By: Achal Channarasappa <achal.channarasappa@cognition.ai>
Co-Authored-By: Achal Channarasappa <achal.channarasappa@cognition.ai>
Co-Authored-By: Achal Channarasappa <achal.channarasappa@cognition.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant